package com.dm.shop.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dm.shop.config.ElasticSearchConfig;
import com.dm.shop.entity.Product;
import com.dm.shop.entity.ProductImage;
import com.dm.shop.service.SearchService;
import com.dm.shop.vo.ProductVO;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @version 1.0
 * @author: Saiya Wang
 * @desc: 商品查找接口的实现类
 * @date: 2021/7/22 8:43
 */
@Service
@Slf4j
public class SearchServiceImpl implements SearchService {

    @Resource(name = "highLevelClient")
    private RestHighLevelClient client;

    @Autowired
    ProductImageServiceImpl productImageService;

    //cid 商品类别 keyword搜索词 pageNo所在页面
    @Override
    public HashMap searchProduct(String keyword, int pageNo, int pageSize, int cid) throws IOException {
        log.info("Service搜索词："+keyword);
        List<ProductVO> productVOList = new ArrayList<>();

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        SearchRequest searchRequest = null;
        //无条件分页查询所有分类
        if(keyword.equals("null") && cid==0)
        {
            searchRequest = new SearchRequest(ElasticSearchConfig.INDEX_NAME);
        }
        //查询指定分类的商品
        else if(cid !=0 && keyword.equals("null"))
        {
            //1.构建指定分类的商品
            searchRequest = new SearchRequest();
            //2.指定要检索的索引库
            searchRequest.indices(ElasticSearchConfig.INDEX_NAME);
            //3.指定检索条件
            sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("cid",cid);
            sourceBuilder.query(termQueryBuilder);
        }
        else if(cid==0 && !keyword.equals("null"))
        {
            //1.构建检索条件
            searchRequest = new SearchRequest();
            //2.指定要检索的索引库
            searchRequest.indices(ElasticSearchConfig.INDEX_NAME);
            //3.指定检索条件
            sourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
            QueryBuilders.matchQuery("keyword",keyword).boost(2f);
            //多条件查询
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword,"name","store");
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            //检索所有分类下面的商品
            boolQueryBuilder.must(multiMatchQueryBuilder);
            //分词查询，boost设置权重
            boolQueryBuilder.should(QueryBuilders.matchQuery("keyword",keyword).boost(2f));
            //拼音查询
            boolQueryBuilder.should(QueryBuilders.matchPhraseQuery("keyword.pinyin",keyword).boost(2f));

            sourceBuilder.query(boolQueryBuilder);
            //4.搜索结果高亮
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            //如果该属性中有多个关键字 则都高亮
            highlightBuilder.requireFieldMatch(true);
            highlightBuilder.field("name");
            highlightBuilder.preTags("<span style='color:#e4393c'>");
            highlightBuilder.postTags("</span>");
            highlightBuilder.field("store");
            highlightBuilder.preTags("<span style='color:#e4393c' >");
            highlightBuilder.postTags("</span>");
            sourceBuilder.highlighter(highlightBuilder);
        }
        //检索指定分类下的商品
        else {
            //1.构建检索条件
            searchRequest = new SearchRequest();
            //2.指定要检索的索引库
            searchRequest.indices(ElasticSearchConfig.INDEX_NAME);
            //3.指定检索条件
            sourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
            QueryBuilders.matchQuery("keyword",keyword).boost(2f);
            //多条件查询
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword,"name","store");
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("cid",cid);
            //检索指定分类下面的商品
            boolQueryBuilder.must(termQueryBuilder);
            boolQueryBuilder.must(multiMatchQueryBuilder);
            //分词查询 boost设置权重
            boolQueryBuilder.should(QueryBuilders.matchQuery("keyword",keyword).boost(2f));
            //拼音查询
            boolQueryBuilder.should(QueryBuilders.matchPhraseQuery("keyword.pinyin",keyword).boost(2f));

            sourceBuilder.query(boolQueryBuilder);

            //4.结果高亮
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.requireFieldMatch(true); //如果该属性中有多个关键字 则都高亮
            highlightBuilder.field("name");
            highlightBuilder.preTags("<span style='color:#dd4b39'>");
            highlightBuilder.postTags("</span>");

            sourceBuilder.highlighter(highlightBuilder);
        }

        //分页
        sourceBuilder.from(pageNo);
        sourceBuilder.size(pageSize);
        //排序规则
        sourceBuilder.sort("saleNum", SortOrder.DESC);//按照销量 降序
        sourceBuilder.sort("collectNum",SortOrder.DESC);//按照收藏量 降序
        sourceBuilder.sort("id",SortOrder.ASC);// 根据编号 升序

        searchRequest.source(sourceBuilder);
        SearchResponse response = client.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
        SearchHit[] hits = response.getHits().getHits();
        //遍历
        for (SearchHit hit : hits)
        {
            //如果不做高亮，则可以直接转为json，然后转为对象
//            String value = hit.getSourceAsString();
//            ESProductTO esProductTO = JSON.parseObject(value, ESProductTO.class);
            //解析高亮字段
            //获取当前命中的对象的高亮的字段
            if (!keyword.equals("null")) //关键字不为空
            {
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField name = highlightFields.get("name");
                String newName = "";
                if (name != null)
                {
                    //获取该高亮字段的高亮信息
                    Text[] fragments = name.getFragments();
                    //将前缀、关键词、后缀进行拼接
                    for (Text fragment : fragments)
                    {
                        newName += fragment;
                    }
                    //将高亮后的值替换掉旧值
                    Map<String,Object> sourceAsMap = hit.getSourceAsMap();
                    sourceAsMap.put("name",newName);
                }
            }
            Map<String,Object> sourceAsMap = hit.getSourceAsMap();
            String json = JSON.toJSONString(sourceAsMap);
            Product product = JSON.parseObject(json,Product.class);
            QueryWrapper<ProductImage> imageQueryWrapper = new QueryWrapper<>();
            imageQueryWrapper.eq("pid",product.getId());

            List<ProductImage> productImages = productImageService.list(imageQueryWrapper);
            List<String> imgUrls = new ArrayList<>();
            if(productImages.size()>0)
            {
                productImages.forEach(p->{
                    imgUrls.add(p.getImgUrl());
                });
            }
            ProductVO productVO = new ProductVO(product,imgUrls);
            productVOList.add(productVO);
        }
        HashMap hashMap = new HashMap<>();
        hashMap.put("list",productVOList);
        hashMap.put("total",response.getHits().getTotalHits().value);
        return hashMap;
    }
}
